// ==UserScript==
// @namespace http://mrk.sed.pl/
// @include https://plus.google.com/*
// @name Google+ Circle Sync
// @description recognize include(circle_name) directive in circle description
// @version 0.3

// ==/UserScript==
// https://plus.google.com/116113476084412126173/about
// by mrk

function sync() {
  var button = document.getElementById('mrk-sync').querySelector('span')
  button.setAttribute('class', 'a-Ba-rd-q')
  var at = OZ_initData[1][15];
  function set() {}
  set.prototype.difference_update = function(a) {for(var k in a) if(typeof a[k]=='boolean') delete this[k]}
  set.prototype.update = function(a) {for(var k in a) if(typeof a[k]=='boolean') this[k]=true}
  set.prototype.copy = function() {var r=new set;for(var k in this) if(typeof this[k]=='boolean') r[k]=true; return r}
  set.prototype.add = function(item) {this[item]=true;}
  set.prototype.items = function(){var out=[];for(var k in this) if(typeof this[k]=='boolean') out.push(k); return out;};
  set.prototype.persons = function(persons){out=[];for(var k in this) if(typeof this[k]=='boolean') out.push(persons[k]); return out;}
  function find_all(re, s){return (s.match(new RegExp(re,'g'))||[]).map(function(s){return s.match(new RegExp(re))})}
  function includes(s) {return find_all('include\\(([^\\)]+)\\)', s).map(function(i) {return i[1]})};
  function encode(payload) {var out=[];for(var k in payload) out.push(k + '=' + encodeURIComponent(payload[k])); return out.join('&')}
  
  function send(url, cb, data) {
    var req = new XMLHttpRequest()
    req.open(data?'POST':'GET',url)
    if(data) {
        req.setRequestHeader('at', at);
        req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=utf-8');
    }
    if(data && typeof data=='object') data=encode(data);
    req.send(data);
    req.onreadystatechange = function() {
        if(this.readyState==4 && this.status==200) {
            cb(eval(this.responseText.slice(4)))
        }
    }
  }
  function add_members(cid, ids, cb) {
      if(!ids.length) return;
      var payload = {
          'a': '[[["'+cid+'"]]]',
          'r': '[[]]',
          'm': '[['+ids.map(function(id){return '[[null,null,"'+id+'"],"",[]]' }).join(',')+']]',
          'at': at
      }
      send('/_/socialgraph/mutate/modifymemberships/?rt=j', cb, payload);
  }
  var by_name={},by_id={},st={},persons={}
  send('/_/socialgraph/lookup/circles/?ct=2&m=1&tag=fg&rt=j', function(data) {
      var d=data[0][0];
      if(d[0]=='tsg.lac') {
          for(var i=0; i<d[1].length; i++) {
            var id=d[1][i][0][0];
            var name=d[1][i][1][0];
            var descr=d[1][i][1][2];
            var c=new set;
            c.name=name;c.id=id;
            c.includes=includes(descr||'')
            by_name[name]=by_id[id]=c;
          }
      }
      for(var i=0; i<d[2].length; i++) {
        var id=d[2][i][0][2];
        if(!id) continue;
        var name=d[2][i][2][0];
        var circles=d[2][i][3];
        persons[id] = name
        for(var j=0; j<circles.length; j++) {
          by_id[circles[j][2][0]].add(id);
        }
      }
      window.to_update = []
      function process(c) {
//          console.log(c.name, c.persons(persons))
          if(c.status==1) {console.warn('circ dep');return;}
          if(c.status==2) {return;}
          c.status=1;
          for(var i in c.includes) process(by_name[c.includes[i]]);
          if(c.includes.length) to_update.push(c);
          c.status=2;
      }
      for(var id in by_id) process(by_id[id]);
      function go(dirty) {
        var c = to_update.shift()
        if(!c) {
            button.setAttribute('class', '');
            if(dirty) window.location.reload();
        }
        var n=c.copy()
        for(var i in c.includes) {n.update(by_name[c.includes[i]])};
        n.difference_update(c);
        if(n.items().length)
          add_members(c.id, n.items(), function(){go(true);})
        else
          go(dirty);
        console.log(c.name, c.persons(persons), ', to add:', n.persons(persons));
        c.update(n);
        return c;
      }
      go(false);
  })

}

function init() {
  var ID = 'mrk-sync-button';
  function test() {
    var script = document.createElement('script')
    script.appendChild(document.createTextNode(sync.toString()))
    script.appendChild(document.createTextNode('sync();'))
    document.body.appendChild(script);
  }
  function install() {
      if(document.getElementById('mrk-sync')) return false;
      var tablist=document.querySelector("[role=tablist]");
      if(!tablist || document.location.href.indexOf('/circles')<0) return true;
      var b = document.createElement('div')
      b.setAttribute('class','a-j-Zc-e')
      b.setAttribute('id', 'mrk-sync')
      b.innerHTML='<span>sync</span>';
//      b.setAttribute('style','float:right');
      b.onclick=test;
      tablist.appendChild(b);
      return false;
  }
  setInterval(install, 1000);
}

init();
